package python

import (
	"embed"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/kagent-dev/kagent/go/cli/internal/mcp"
	"github.com/kagent-dev/kagent/go/cli/internal/mcp/frameworks/common"
	"github.com/stoewer/go-strcase"
)

//go:embed all:templates
var templateFiles embed.FS

// Generator for Python projects
type Generator struct {
	common.BaseGenerator
}

// NewGenerator creates a new Python generator
func NewGenerator() *Generator {
	return &Generator{
		BaseGenerator: *common.NewBaseGenerator(templateFiles, "src/tools/tool.py.tmpl"),
	}
}

// GenerateProject generates a new Python project
func (g *Generator) GenerateProject(config mcp.ProjectConfig) error {
	if config.Verbose {
		fmt.Println("Generating FastMCP Python project...")
	}

	if err := g.BaseGenerator.GenerateProject(config); err != nil {
		return fmt.Errorf("failed to generate project: %w", err)
	}

	return nil
}

// GenerateTool generates a new tool for a Python project.
func (g *Generator) GenerateTool(projectroot string, config mcp.ToolConfig) error {
	if err := g.BaseGenerator.GenerateTool(projectroot, config); err != nil {
		return fmt.Errorf("failed to generate tool: %w", err)
	}

	// After generating the tool file, regenerate the __init__.py file
	toolsDir := filepath.Join(projectroot, "src", "tools")
	if err := g.regenerateToolsInit(toolsDir); err != nil {
		return fmt.Errorf("failed to regenerate __init__.py: %w", err)
	}

	toolNameSnakeCase := strcase.SnakeCase(config.ToolName)

	fmt.Printf("✅ Successfully created tool: %s\n", config.ToolName)
	fmt.Printf("📁 Generated file: src/tools/%s.py\n", toolNameSnakeCase)
	fmt.Printf("🔄 Updated tools/__init__.py with new tool import\n")

	fmt.Printf("\nNext steps:\n")
	fmt.Printf("1. Edit src/tools/%s.py to implement your tool logic\n", toolNameSnakeCase)
	fmt.Printf("2. Configure any required environment variables in manifest.yaml\n")
	fmt.Printf("3. Run 'uv run python src/main.py' to start the server\n")
	fmt.Printf("4. Run 'uv run pytest tests/' to test your tool\n")

	return nil
}

// regenerateToolsInit regenerates the __init__.py file in the tools directory
func (g *Generator) regenerateToolsInit(toolsDir string) error {
	// Scan the tools directory for Python files
	tools, err := g.scanToolsDirectory(toolsDir)
	if err != nil {
		return fmt.Errorf("failed to scan tools directory: %w", err)
	}

	// Generate the __init__.py content
	content := g.generateInitContent(tools)

	// Write the __init__.py file
	initPath := filepath.Join(toolsDir, "__init__.py")
	return os.WriteFile(initPath, []byte(content), 0644)
}

// scanToolsDirectory scans the tools directory and returns a list of tool names
func (g *Generator) scanToolsDirectory(toolsDir string) ([]string, error) {
	var tools []string

	// Read the directory
	entries, err := os.ReadDir(toolsDir)
	if err != nil {
		return nil, fmt.Errorf("failed to read tools directory: %w", err)
	}

	// Find all Python files (excluding __init__.py)
	for _, entry := range entries {
		if entry.IsDir() {
			continue
		}

		name := entry.Name()
		if strings.HasSuffix(name, ".py") && name != "__init__.py" {
			// Extract tool name (filename without .py extension)
			toolName := strings.TrimSuffix(name, ".py")
			tools = append(tools, toolName)
		}
	}

	return tools, nil
}

// generateInitContent generates the content for the __init__.py file
func (g *Generator) generateInitContent(tools []string) string {
	var content strings.Builder

	// Add the header comment
	content.WriteString(`"""Tools package for knowledge-assistant MCP server.

This file is automatically generated by the dynamic loading system.
Do not edit manually - it will be overwritten when tools are loaded.
"""

`)

	// Add import statements
	for _, tool := range tools {
		content.WriteString(fmt.Sprintf("from .%s import %s\n", tool, tool))
	}

	// Add empty line
	content.WriteString("\n")

	// Add __all__ list
	content.WriteString("__all__ = [")
	for i, tool := range tools {
		if i > 0 {
			content.WriteString(", ")
		}
		content.WriteString(fmt.Sprintf(`"%s"`, tool))
	}
	content.WriteString("]\n")

	return content.String()
}
